﻿#region Legal

// Jimmy Zimmerman
// Team Mongoose
//
// END USER LICENSE AGREEMENT
// IMPORTANT - READ THIS CAREFULLY:  This End User License Agreement is a legal agreement
// between you (either an individual, business entity, currently active identity of an
// individual with Multiple Personality Disorder, octopus overlord, or otherworldly entity),
// and Team Mongoose, for the enclosed, components.
//
// By reading this document and/or installing this product, you agree to be bound by the
// terms of this EULA.
//
// Team Mongoose owns all copyright, trade secret, trademark, trade wars,
// patent, portent, and potential rights to these components.  Team Mongoose
// grants you the right to deploy the enclosed components.
//
// If you agree to the terms of this EULA, a license to use these components is granted to you.
//
// If you should happen to benefit from the enclosed components, then you are legally and
// contractually bound to thank us for it. Send your regards to jimmyzimms@hotmail.com.
//
// OTHER RIGHTS AND LIMITATONS
// You may not reverse-engineer, decompile, decode, disassemble, psycho-analyze, or otherwise
// attempt to find hidden meanings between the lines of this EULA; unless, of course, you should
// happen to find some, and they are funny.
//
// You may not translate this EULA or any part of the components into Ancient Sumerian.
//
// THESE COMPONENTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. ANY USE OF THE COMPONENTS
// CONTAINED IS AT YOUR OWN RISK. TEAM MONGOOSE DISCLAIM ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, WITH RESPECT TO THE ACCURRACY AND CORRECTNESS OF THE COMPONENTS CONTAINED
// HEREIN. TEAM MONGOOSE DOES NOT WARRANT THAT THE COMPONENTS ARE FLAWLESS.
//
// REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED
// PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
// * REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE
// * REDISTRIBUTIONS IN BINARY FORM MUST NOTE THE USE OF THE COMPONENT IN DOCUMENTATION AND/OR
//   OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
// * NEITHER THE NAME OF TEAM MONGOOSE MAY BE USED TO ENDORES OR PROMOTE PRODUCTS
//   DERIVED FROM THIS SOFTWARE WITHOUT SPECIFIC PRIOR WRITTEN PERMISSION.
//
// IN NO EVENT SHALL TEAM MONGOOSE BE HELD LIABLE FOR INCIDENTAL, SPECIAL, INDIRECT,
// INCONSEQUENTIAL, UNBELIEVABLE, EXAGGERATED, VERBOSE, OR TYPICAL DAMAGES INCURRED WHILE USING
// THE ENCLOSED COMPONENTS.
//
// OUR STUFF ALWAYS WORKS - SOMETIMES.

#endregion

using Microsoft.AnalysisServices.AdomdClient;
using System.ComponentModel;
using System.Data.Common;
using System.Linq;

namespace System.Data.MdxClient
{
    /// <summary>
    /// Represents a MDX connection to a SQL Server Analysis Services database.
    /// </summary>
    public class MdxConnection : DbConnection
    {
        #region Fields

        private readonly AdomdConnection connection = new AdomdConnection();

        #endregion

        #region Properties

        /// <summary>
        /// Gets the internal, native ADOMD.NET connection object.
        /// </summary>
        /// <value>An <see cref="AdomdConnection"/> object.</value>
        protected internal AdomdConnection AdomdConnection
        {
            get
            {
                return this.connection;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the object has been disposed.
        /// </summary>
        /// <value>True if the object has been disposed; otherwise, false.</value>
        protected bool IsDisposed
        {
            get;
            private set;
        }

        #endregion

        #region Overrides

        /// <summary>
        /// Begins a database transaction.
        /// </summary>
        /// <param name="isolationLevel">One of the <see cref="IsolationLevel"/> values.</param>
        /// <returns>A <see cref="MdxTransaction"/> object.</returns>
        protected override DbTransaction BeginDbTransaction( IsolationLevel isolationLevel )
        {
            var transaction = this.connection.BeginTransaction( isolationLevel );
            return new MdxTransaction( this, transaction );
        }

        /// <summary>
        /// Changes the current database the connection is attached to.
        /// </summary>
        /// <param name="databaseName">The name of the new, target database.</param>
        public override void ChangeDatabase( string databaseName )
        {
            this.connection.ChangeDatabase( databaseName );
        }

        /// <summary>
        /// Closes the connection.
        /// </summary>
        public override void Close()
        {
            this.connection.Close();
        }

        /// <summary>
        /// Gets or sets the connection string.
        /// </summary>
        /// <value>The connection string.</value>
        public override string ConnectionString
        {
            get
            {
                return this.connection.ConnectionString;
            }
            set
            {
                this.connection.ConnectionString = value;
            }
        }

        /// <summary>
        /// Gets the connection time out in seconds.
        /// </summary>
        /// <value>The connection timeout in seconds.</value>
        public override int ConnectionTimeout
        {
            get
            {
                return this.connection.ConnectionTimeout;
            }
        }

        /// <summary>
        /// Gets the name of the current database.
        /// </summary>
        /// <value>The name of the current database.</value>
        public override string Database
        {
            get
            {
                return this.connection.Database;
            }
        }

        /// <summary>
        /// Gets the name of the current server.
        /// </summary>
        /// <value>The name of the current server.</value>
        public override string DataSource
        {
            get
            {
                // TODO: extract from connection string
                return string.Empty;
            }
        }

        /// <summary>
        /// Creates and returns a new database command.
        /// </summary>
        /// <returns>A <see cref="MdxCommand"/> object.</returns>
        protected override DbCommand CreateDbCommand()
        {
            var command = this.DbProviderFactory.CreateCommand();
            command.Connection = this;
            return command;
        }

        /// <summary>
        /// Gets a provider factory for the connection.
        /// </summary>
        /// <value>A <see cref="MdxClientFactory"/> object.</value>
        protected override DbProviderFactory DbProviderFactory
        {
            get
            {
                return MdxClientFactory.Instance;
            }
        }

        /// <summary>
        /// Releases the managed and, optionally, the unmanaged resources used by the <see cref="MdxConnection"/> class.
        /// </summary>
        /// <param name="disposing">Indicates whether the object is being disposed.</param>
        protected override void Dispose( bool disposing )
        {
            if ( this.IsDisposed )
                return;

            this.IsDisposed = true;
            this.Close();
            this.connection.Dispose();
            base.Dispose( disposing );
        }

        /// <summary>
        /// Gets a schema table for the connection.
        /// </summary>
        /// <returns>A <see cref="DataTable"/> object.</returns>
        /// <remarks>The default implementation returns the database catalog information.</remarks>
        public override DataTable GetSchema()
        {
            var tables = this.connection.GetSchemaDataSet( AdomdSchemaGuid.Catalogs, new object[0] ).Tables;

            if (tables == null) return null;
            return tables.Cast<DataTable>().FirstOrDefault();
        }

        /// <summary>
        /// Gets a schema talbe for the specified collection.
        /// </summary>
        /// <param name="collectionName">The name of the schema collection to return.</param>
        /// <returns>A <see cref="DataTable"/> object.</returns>
        public override DataTable GetSchema( string collectionName )
        {
            var tables = this.connection.GetSchemaDataSet( collectionName, new AdomdRestrictionCollection(), false ).Tables;

            if (tables == null) return null;
            return tables.Cast<DataTable>().FirstOrDefault();
        }

        /// <summary>
        /// Gets a schema talbe for the specified collection.
        /// </summary>
        /// <param name="collectionName">The name of the schema collection to return.</param>
        /// <param name="restrictionValues">The values to restrict from the returned schema.</param>
        /// <returns>A <see cref="DataTable"/> object.</returns>
        public override DataTable GetSchema( string collectionName, string[] restrictionValues )
        {
            var restrictions = new AdomdRestrictionCollection();

            //if ( restrictionValues != null )
            //{
                // TODO: is this correct?
                foreach ( var value in restrictionValues )
                    restrictions.Add( new AdomdRestriction( value, null ) );
            //}

            var tables = this.connection.GetSchemaDataSet( collectionName, restrictions, false ).Tables;

            if (tables == null) return null;
            return tables.Cast<DataTable>().FirstOrDefault();
        }

        /// <summary>
        /// Opens the connection.
        /// </summary>
        public override void Open()
        {
            this.connection.Open();
        }

        /// <summary>
        /// Gets the version number reported by the server.
        /// </summary>
        /// <value>The server version number.</value>
        public override string ServerVersion
        {
            get
            {
                return this.connection.ServerVersion;
            }
        }

        /// <summary>
        /// Gets the connection state.
        /// </summary>
        /// <value>One of the <see cref="ConnectionState"/> values.</value>
        public override ConnectionState State
        {
            get
            {
                return this.connection.State;
            }
        }

        /// <summary>
        /// Returns a lifetime service object to maintain the lifetime of the component.
        /// </summary>
        /// <returns>A lifetime service object.</returns>
        public override object InitializeLifetimeService()
        {
            return this.connection.InitializeLifetimeService();
        }

        /// <summary>
        /// Gets or sets the site of the component.
        /// </summary>
        /// <value>An <see cref="ISite"/> object.</value>
        public override ISite Site
        {
            get
            {
                return this.connection.Site;
            }
            set
            {
                this.connection.Site = value;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnection"/> class.
        /// </summary>
        /// <param name="connection">The adpated <see cref="AdomdConnection"/> object.</param>
        /// <exception cref="ArgumentNullException"><paramref name="connection"/> is null.</exception>
        protected internal MdxConnection( AdomdConnection connection )
        {
            if ( ( this.connection = connection ) == null )
                throw new ArgumentNullException( "connection" );
        }

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnection"/> class.
        /// </summary>
        public MdxConnection()
            : this( new AdomdConnection() )
        {
        }

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnection"/> class.
        /// </summary>
        /// <param name="connectionString">The connection string for the connection.</param>
        public MdxConnection( string connectionString )
            : this( new AdomdConnection( connectionString ) )
        {
        }

        #endregion
    }
}
